package project3;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Util {
	public static final String ADDRESS = "http://homes.cs.washington.edu/~yuxuan/shawnserverip/getipport.php";
	
	public enum CellType {
		NULL, // don't use this one
		CREATE,
		CREATED,
		RELAY, 
		DESTROY, 
		OPEN, 
		OPENED,
		OPEN_FAILED,
		CREATE_FAILED
	}

	public static final int CELL_LENGTH = 512;
	
	// returns the circuit id of a cell in a 512-byte long array
	public static int readCircId(byte[] data) {
		return getIntFromTwoBytes(data, 0);
	}
	
	// returns the cell type in a 512-byte long array
	public static CellType readCellType(byte[] data) {
		int cellTypeId = ((int) data[2] & 0xff);
		return CellType.values()[cellTypeId];
	}
	
	public static byte[] generateHeader(int circId, CellType cellType, int length) {
		byte[] ret = new byte[length];
		ret[0] = (byte) ((circId >> 8) & 0xFF);
		ret[1] = (byte) (circId & 0xFF);
		ret[2] = (byte) ((cellType.ordinal()) & 0xFF);
		return ret;
	}
	
	public static int getIntFromTwoBytes(byte[] data, int startPoint) {
		return ((int) (data[startPoint] & 0xFF) << 8) + ((int) (data[startPoint + 1] & 0xFF)) ;
	}
	
	public static long getLongFromFourBytes(byte[] data, int startPoint) {
		return ((long) (data[startPoint] & 0xFF) << 24) + ((long) (data[startPoint + 1] & 0xFF) << 16) 
				+ ((long) (data[startPoint + 2] & 0xFF) << 8) + ((long) (data[startPoint + 3] & 0xFF)) ;
	}
	
	public static String getOutAddressPort() throws Exception {
		String ret = "";
        URL ip = null;
        ip = new URL(ADDRESS);
        URLConnection yc = null;
		yc = ip.openConnection();
        BufferedReader in = null;
        in = new BufferedReader(
			                        new InputStreamReader(
			                        yc.getInputStream()));
        String inputLine;

        while ((inputLine = in.readLine()) != null) 
        	ret += inputLine;
        
		in.close();
        return ret;
	}
	
	// takes a data array and cut it into relay data cells addressed to specified circuitId / streamId
	public static List<RelayCell> cutByteArrayIntoCells (byte[] arr, int circuitId, int streamId) {
		
		List<RelayCell> result = new ArrayList<RelayCell>();
		
		int numWholeBlocks = arr.length / 498;
		int lengthOfTail = arr.length % 498; // the number of remaining bytes
		
		for (int i = 0; i < numWholeBlocks; i++) {
			int bodyLength;
			
			if (i == numWholeBlocks - 1 && lengthOfTail == 0)
				// special value to indicate the end of the request data cell sequence
				bodyLength = Util.CELL_LENGTH - RelayCell.BODY_OFFSET + 1;
			else
				bodyLength = Util.CELL_LENGTH - RelayCell.BODY_OFFSET;
				
			// create a relay cell from a chunk of the byte array 
			RelayCell relayCell = new RelayCell(circuitId,
					streamId,
					bodyLength,
					(byte) 0x02, // RELAY DATA CMD
					Arrays.copyOfRange(arr,
								i * 498,
								(i+1) * 498));
			
			result.add(relayCell);
		}
		
		if (lengthOfTail > 0) { // put the remaining bytes into another cell
			int bodyLength = arr.length - numWholeBlocks * 498;
					
			RelayCell relayCell = new RelayCell(circuitId,
					streamId,
					bodyLength,
					(byte) 0x02, // RELAY DATA CMD
					Arrays.copyOfRange(arr,
							numWholeBlocks * 498,
							arr.length));			
			
			result.add(relayCell);
		}
		
		return result;
	}
}
